package main

import (
	"flag"
	"fmt"
	"../common"
	"os"
	"runtime"
	"syscall"

	logging "github.com/op/go-logging"
)

var version string = "20170915.0"

func init() {
	var logBackend = logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetFormatter(format)
	logging.SetBackend(logBackend)
	log.Info("The current version is:", version)
	runtime.GOMAXPROCS(1)
	common.SetLogger(log)
}

//}

var format = logging.MustStringFormatter(
	`%{color}%{time:2006-01-02 15:04:05.000} %{shortfunc} %{level:.4s} %{id:03x}%{color:reset} %{message}`)
var log = logging.MustGetLogger("golia")

func Main() {
	var config_file string
	var version_only bool
	var with_module bool
	var debug_mode bool
	flag.StringVar(&config_file, "conf", DEFAULT_CONFIG, "conf file name")
	flag.BoolVar(&version_only, "version", false, "version info")
	flag.BoolVar(&with_module, "with_module", true, "run with module")
	flag.BoolVar(&debug_mode, "debug_mode", false, "run in debug mode")

	flag.Parse()
	if flag.NArg() == 1 {
		config_file = flag.Arg(0)
	}

	if version_only {
		fmt.Println(version)
		return
	}

	log.Infof("golia starting, using config :%s", config_file)
	conf, err := LoadFile(config_file)
	if err != nil {
		log.Errorf("Error parsing config %s: %s", config_file, err)
		return
	}

	if debug_mode {
		conf.LogLevel = logging.DEBUG
		conf.LogToFile = false
		with_module = false
	}

	if conf.LogToFile == true {
		if logFile, err := os.OpenFile(DEFAULT_LOG, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644); err != nil {
			log.Errorf("set log file error %s : %s \n", DEFAULT_LOG, err)
			logging.Reset()
			//return
		} else {
			log.Info("set logBackend to file")
			//logging.Reset()
			var logBackend = logging.NewLogBackend(logFile, "", 0)
			logging.SetFormatter(format)
			logging.SetBackend(logBackend)
		}
	}

	logging.SetLevel(conf.LogLevel, "golia")
	log.Infof("loglevel: %v", logging.GetLevel("golia"))

	sender, err := NewSender(conf)
	if err != nil {
		log.Errorf("init sender error %v", err)
		return
	}

	var jobs []common.Job
	for _, module := range conf.Modules {
		switch module {
		case "ChangeDetector":
			cdt, err := common.NewChangeDetector(UPDATEABLES)
			if err != nil {
				log.Errorf("error when init change detector: %v", err)
				continue
			}
			jobs = append(jobs, cdt)
		case "Heartbeater":
			hbt := common.HeartBeater{
				Checker: sender.HasError,
			}
			jobs = append(jobs, &hbt)
		}
	}
	workshop := common.WorkShop{
		BaseUrl:       conf.MondoAddr,
		Interval:      conf.MetricInterval,
		Info:          version,
		ExitAction:    GetExitAction(),
		RestartAction: GetRestartAction(),
		Jobs:          jobs,
	}

	if with_module {
		workshop.Start()
		go workshop.Run()
		defer workshop.Stop()
	}

	go sender.Run()
	defer sender.Stop()
	common.WritePidFile(DEFAULT_PID)

	if err := Wait(); err != nil {
		log.Fatal("error when syscall", err)
	}
}

func main() {
	if ppid, err := GetEnvs(); err == nil {
		log.Infof("should kill parent process: %d", ppid)
		if err := KillParent(ppid); nil != err {
			log.Fatal("kill parent process error: %v", err)
			os.Exit(2)
		}
	}
	Main()
}

func ForkExec() error {
	argv0, err := common.LookPath()
	if nil != err {
		return err
	}

	wd, err := os.Getwd()
	if nil != err {
		return err
	}
	if err := os.Setenv("GOLIA_PID", ""); nil != err {
		return err
	}
	if err := os.Setenv("GOLIA_PPID", fmt.Sprint(syscall.Getpid())); nil != err {
		return err
	}
	files := make([]*os.File, 3)
	files[syscall.Stdin] = os.Stdin
	files[syscall.Stdout] = os.Stdout
	files[syscall.Stderr] = os.Stderr

	p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
		Dir:   wd,
		Env:   os.Environ(),
		Files: files,
		Sys:   &syscall.SysProcAttr{},
	})

	if nil != err {
		return err
	}
	log.Infof("spawned child %v", p.Pid)
	if err = os.Setenv("GOLIA_PID", fmt.Sprint(p.Pid)); nil != err {
		return err
	}
	return nil
}

func GetEnvs() (ppid int, err error) {
	if _, err = fmt.Sscan(os.Getenv("GOLIA_PPID"), &ppid); nil != err {
		return
	}
	log.Info("new process start")
	return
}

// Send SIGQUIT to the given ppid in order to complete the handoff to the
// child process.
